﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace TrainingViewer
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            n1 = 0;
            n0 = 0;
            newSessionWindow = new SimpleClassifierMenu();
            svmSessionWindow = new SVMMenu();
            adaBoostSesssionWindows = new AdaBoostMenu();
            chart1.Series.Clear();
        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
        
        }

        private void initiateSimpleClassifierTraining(
            ref AutomationInterfaceLib.LinearClassifiersWrapper engine, AutomationInterfaceLib.TClassifierType classifierType,
            bool hasToUseRestriction, double regularizaton )
        {
            //начинаем новую сессию
            engine.Reset();
            //генерация точек
            Array arrayOfLines = (Array)lines.ToArray();
            Array arrayOfCircles = (Array)circles.ToArray();
            engine.DoGeneratePoints(Convert.ToInt32(n1), Convert.ToInt32(n0), arrayOfLines, arrayOfCircles );
            //обучение
            engine.Train( classifierType, hasToUseRestriction, regularizaton );
            //берем результаты
            engine.GetResults(out lineParams);
                
        }

        private void initiateSVMTraining(ref AutomationInterfaceLib.SVMWrapper svmEngine, AutomationInterfaceLib.TKernelType kernelType,
            decimal penaltyCoefficient )
        {
            //начинаем новую сессию
            svmEngine.Reset();
            //генерация точек
            Array arrayOfLines = (Array)lines.ToArray();
            Array arrayOfCircles = (Array)circles.ToArray();
            svmEngine.DoGeneratePoints(Convert.ToInt32(n1), Convert.ToInt32(n0), arrayOfLines, arrayOfCircles);
            //обучение
            svmEngine.Train( Convert.ToDouble( penaltyCoefficient ), kernelType, 1 );
            //берем результаты
            svmEngine.GetResults(out lineParams);

        }

        //проверят на пересечение с кубом [0,1]*[0,1] и выдает координаты пересечения с кубом
        private bool getTwoPointsFromLineParams(out double firstX, out double firstY,
            out double secondX, out double secondY, AutomationInterfaceLib.CLine line )
        {
            firstX = 0;
            firstY = 0;
            secondX = 0;
            secondY = 0;
            const double eps = 0.000000000000000000001;
            if (Math.Abs(line.A) < eps && Math.Abs(line.B) < eps) {
                //какая-то ошибка
                return false;
            }
            //особые случаи
            if( Math.Abs( line.A ) < eps ) {
                //уравнение y = const
                double curConst = -line.C / line.B;
                if( curConst >=0 && curConst <=1 ) {
                    //правильная линия
                    firstX = 0;
                    firstY = curConst;
                    secondX = 1;
                    secondY = curConst;
                }
                return true;
            }
            if( Math.Abs( line.B ) < eps ) {
                //уравнение x = const
                double curConst = -line.C / line.A;
                if (curConst >= 0 && curConst <= 1)
                {
                    //правильная линия
                    firstX = curConst;
                    firstY = 0;
                    secondX = curConst;
                    secondY = 1;
                }
                return true;
            }
            //не особые случай. Проверим какие из четырех сторон пересекает линия
            double top = -(line.C + line.B) / line.A;
            double bottom = -line.C / line.A;
            double left = -line.C / line.B;
            double right = -(line.C + line.A) / line.B;

            bool hasTopIntersection = top >= 0 && top <= 1;
            bool hasBottomIntersection = bottom >= 0 && bottom <= 1;
            bool hasLeftIntersection = left >= 0 && left <= 1;
            bool hasRightIntersection = right >= 0 && right <= 1;

            if( !hasTopIntersection
                && !hasBottomIntersection
                && !hasLeftIntersection
                && !hasRightIntersection ) {
                return false;
            }

            bool isFirstGenerated = false;

            if (hasTopIntersection) {
                if( !isFirstGenerated ) {
                    firstX = top;
                    firstY = 1;
                    isFirstGenerated = true;
                } else {
                    secondX = top;
                    secondY = 1;
                }
            }

            if (hasBottomIntersection)
            {
                if (!isFirstGenerated)
                {
                    firstX = bottom;
                    firstY = 0;
                    isFirstGenerated = true;
                }
                else
                {
                    secondX = bottom;
                    secondY = 0;
                }
            }

            if (hasLeftIntersection)
            {
                if (!isFirstGenerated)
                {
                    firstX = 0;
                    firstY = left;
                    isFirstGenerated = true;
                }
                else
                {
                    secondX = 0;
                    secondY = left;
                }
            }

            if (hasRightIntersection)
            {
                if (!isFirstGenerated)
                {
                    firstX = 1;
                    firstY = right;
                    isFirstGenerated = true;
                }
                else
                {
                    secondX = 1;
                    secondY = right;
                }
            }
            
            return true;
        }

        //рисует точки на графике
        private void drawPoints( ref System.Array points )
        {           
            //вытаскиваем интерфейс для перечисления точек
            IEnumerable<AutomationInterfaceLib.C2DPoint> enumarble = points.Cast<AutomationInterfaceLib.C2DPoint>();
            IEnumerator<AutomationInterfaceLib.C2DPoint> enumerator = enumarble.GetEnumerator();
            //начинаем рисовать график
            chart1.BeginInit();
            chart1.Series.Clear();
            //подстраиваем оси к кубу [0,1]*[0,1]
            chart1.ChartAreas[0].AxisX.Minimum = 0;
            chart1.ChartAreas[0].AxisX.Maximum = 1;
            chart1.ChartAreas[0].AxisY.Minimum = 0;
            chart1.ChartAreas[0].AxisY.Maximum = 1;

            chart1.Series.Add("Cloud1");
            //точки с положительным фактором наверх, с отрицательным вниз
            while ( enumerator.MoveNext())
            {
                AutomationInterfaceLib.C2DPoint curPoint = enumerator.Current;
                if( curPoint.Factor > 0 ) {
                    chart1.Series["Cloud1"].Points.AddXY(curPoint.X, curPoint.Y);
                }
                
            }
            //параметры графика
            chart1.Series["Cloud1"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.FastPoint;
            chart1.Series["Cloud1"].Color = Color.Blue;
           
            chart1.Series.Add("Cloud2");
            enumerator.Reset();
            while (enumerator.MoveNext())
            {
                AutomationInterfaceLib.C2DPoint curPoint = enumerator.Current;
                if (curPoint.Factor < 0)
                {
                    chart1.Series["Cloud2"].Points.AddXY(curPoint.X, curPoint.Y);
                }
            }
            chart1.Series["Cloud2"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.FastPoint;
            chart1.Series["Cloud2"].Color = Color.Red;

            //закончили рисовать
            chart1.EndInit();
        }

        //рисует точки на графике
        private void drawCurve( ref System.Array points )
        {            
            //вытаскиваем интерфейс для перечисления точек
            IEnumerable<AutomationInterfaceLib.C2DPoint> enumarble = points.Cast<AutomationInterfaceLib.C2DPoint>();
            IEnumerator<AutomationInterfaceLib.C2DPoint> enumerator = enumarble.GetEnumerator();
            //начинаем рисовать график
            chart1.BeginInit();
            //подстраиваем оси к кубу [0,1]*[0,1]
            chart1.ChartAreas[0].AxisX.Minimum = 0;
            chart1.ChartAreas[0].AxisX.Maximum = 1;
            chart1.ChartAreas[0].AxisY.Minimum = 0;
            chart1.ChartAreas[0].AxisY.Maximum = 1;

            chart1.Series.Add("Curve");
            //точки с положительным фактором наверх, с отрицательным вниз
            while (enumerator.MoveNext())
            {
                AutomationInterfaceLib.C2DPoint curPoint = enumerator.Current;
                chart1.Series["Curve"].Points.AddXY(curPoint.X, curPoint.Y);
            }
            //параметры графика
            chart1.Series["Curve"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.FastPoint;
            chart1.Series["Curve"].Color = Color.FromArgb(50, Color.Black );

            //закончили рисовать
            chart1.EndInit();
        }

        private void chart1_Click(object sender, EventArgs e)
        {

        }

        private void newSVMSessionToolStripMenuItem_Click(object sender, EventArgs e)
        {
           
        }

        private void sVMToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (svmSessionWindow.ShowDialog() == DialogResult.OK)
            {
                svmSessionWindow.DialogResult = DialogResult.No;
                //получим параметры обучения
                n1 = svmSessionWindow.GetN1Count();
                n0 = svmSessionWindow.GetN0Count();
                svmSessionWindow.GetLinesParams(out lines);
                svmSessionWindow.GetCircleParams(out circles);
                decimal penaltyCoefficient = svmSessionWindow.GetPenaltyCoefficient();
                AutomationInterfaceLib.TKernelType kernelType = svmSessionWindow.GetSVMKernelType();
                toolStripStatusLabel1.Text = "Обучаю классификатор...";
                statusStrip1.Update();

                AutomationInterfaceLib.SVMWrapper svmEngine = new AutomationInterfaceLib.SVMWrapper();

                try
                {
                    initiateSVMTraining(ref svmEngine, kernelType, penaltyCoefficient);
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Ошибка при обучении классификатора. Нужна отладка");
                    svmEngine.Reset();
                    return;
                }
                //нарисуем точки
                toolStripStatusLabel1.Text = "Рисую точки...";
                statusStrip1.Update();

                System.Array generatedPoints;
                svmEngine.GetGeneratedPoint(out generatedPoints);
                drawPoints(ref generatedPoints);

                System.Array curvePoints;
                svmEngine.GetCurvePoints(out curvePoints);
                drawCurve(ref curvePoints);

                toolStripStatusLabel1.Text = "Готово.";
                statusStrip1.Update();
            }
        }

        private void простойЛинейныйКлассификаторToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (newSessionWindow.ShowDialog() == DialogResult.OK)
            {
                newSessionWindow.DialogResult = DialogResult.No;
                //получим параметры обучения
                n1 = newSessionWindow.GetN1Count();
                n0 = newSessionWindow.GetN0Count();
                newSessionWindow.GetLinesParams(out lines);
                newSessionWindow.GetCircleParams(out circles);
                AutomationInterfaceLib.TClassifierType classifierType = newSessionWindow.GetClassifierType();
                bool hasToUseRestriction = newSessionWindow.HasRestriction();
                double regularizaton = newSessionWindow.GetRegularization();
                toolStripStatusLabel1.Text = "Обучаю классификатор...";
                statusStrip1.Update();

                AutomationInterfaceLib.LinearClassifiersWrapper engine =
                    new AutomationInterfaceLib.LinearClassifiersWrapper();
                try
                {
                    initiateSimpleClassifierTraining( ref engine, classifierType, hasToUseRestriction, regularizaton );
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Ошибка при обучении классификатора. Нужна отладка");
                    engine.Reset();
                    return;
                }
                //нарисуем точки
                toolStripStatusLabel1.Text = "Рисую точки...";
                statusStrip1.Update();

                System.Array generatedPoints;
                engine.GetGeneratedPoint(out generatedPoints);
                drawPoints(ref generatedPoints);

                System.Array curvePoints;
                engine.GetCurvePoints(out curvePoints);
                drawCurve(ref curvePoints);

                toolStripStatusLabel1.Text = "Готово.";
                statusStrip1.Update();
            }
        }

        private void adaBoostToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (adaBoostSesssionWindows.ShowDialog() == DialogResult.OK)
            {
                adaBoostSesssionWindows.DialogResult = DialogResult.No;
                //получим параметры обучения
                n1 = adaBoostSesssionWindows.GetN1Count();
                n0 = adaBoostSesssionWindows.GetN0Count();
                adaBoostSesssionWindows.GetLinesParams(out lines);
                adaBoostSesssionWindows.GetCircleParams(out circles);
                decimal classifiersCount = adaBoostSesssionWindows.GetClassifiersCount();
                toolStripStatusLabel1.Text = "Обучаю классификатор...";
                statusStrip1.Update();

                AutomationInterfaceLib.AdaBoostWrapper adaBoostMachine = new AutomationInterfaceLib.AdaBoostWrapper();
                try
                {
                    initiateAdaBoostTraining( ref adaBoostMachine, classifiersCount );
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Ошибка при обучении классификатора. Нужна отладка");
                    return;
                }
                //нарисуем точки
                toolStripStatusLabel1.Text = "Рисую точки...";
                statusStrip1.Update();
                
                System.Array generatedPoints;
                adaBoostMachine.GetGeneratedPoint(out generatedPoints);
                drawPoints( ref generatedPoints );

                System.Array curvePoints;
                adaBoostMachine.GetCurvePoints( out curvePoints );
                drawCurve(ref curvePoints );

                //выведем ее
                toolStripStatusLabel1.Text = "Готово.";
                statusStrip1.Update();
            }
        }

        private void initiateAdaBoostTraining( ref AutomationInterfaceLib.AdaBoostWrapper engine, decimal classifiersCount )
        {
            //начинаем новую сессию
            engine.Reset();
            //генерация точек
            Array arrayOfLines = (Array)lines.ToArray();
            Array arrayOfCircles = (Array)circles.ToArray();
            engine.DoGeneratePoints(Convert.ToInt32(n1), Convert.ToInt32(n0), arrayOfLines, arrayOfCircles);
            //обучение
            engine.Train( Convert.ToInt32( classifiersCount ) );
        }

       
             
    }

   
}
